﻿using System;
using System.IO;
using System.Data;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Runtime.Serialization;
using System.Security.Permissions;

namespace MockCommon
{
    [Serializable()]
    public class CsvDataSet : DataSet
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="CsvDataSet"/> class.
        /// </summary>
        public CsvDataSet()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CsvDataSet"/> class.
        /// </summary>
        /// <param name="info">The info.</param>
        /// <param name="context">The context.</param>
        protected CsvDataSet(SerializationInfo info, StreamingContext context) :
            base(info, context)
        {
        }

        /// <summary>
        /// Populates a serialization information object with the data needed to serialize the <see cref="T:System.Data.DataSet"/>.
        /// </summary>
        /// <param name="info">A <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized data associated with the <see cref="T:System.Data.DataSet"/>.</param>
        /// <param name="context">A <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains the source and destination of the serialized stream associated with the <see cref="T:System.Data.DataSet"/>.</param>
        /// <exception cref="T:System.ArgumentNullException">The <paramref name="info"/> parameter is null.</exception>
        [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
        public override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Text", "");
            base.GetObjectData(info, context);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CsvDataSet"/> class.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <param name="pattern">The pattern.</param>
        public CsvDataSet(string path, string pattern)
        {
            StreamReader reader = new StreamReader(path);
            Load(reader, pattern);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CsvDataSet"/> class.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="pattern">The pattern.</param>
        public CsvDataSet(Stream stream, string pattern)
        {
            StreamReader reader = new StreamReader(stream);
            Load(reader, pattern);
        }

        /// <summary>
        /// Loads the specified reader.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <param name="pattern">The pattern.</param>
        public void Load(TextReader reader, string pattern)
        {
            if (pattern == null)
            {
                throw new MockException("Pattern is null", "Common.Load");
            }

            DataTable table = new DataTable();
            table.Locale = CultureInfo.CurrentCulture;
            string linePattern = ConvertPattern(pattern);
            if (linePattern == null)
                throw new MockException("Line pattern is null", "Common.Load");
            try
            {
                if (reader != null)
                {
                    string line = "";
                    string[] parts = null;
                    int index = 0;
                    DataColumn column;
                    DataRow row;
                    int columnCount = 0;
                    while ((line = reader.ReadLine()) != null)
                    {
                        if (index == 0)
                        {
                            if (!Regex.IsMatch(line, pattern))
                            {
                                throw new MockException("Data is invalid", "Common.Load");
                            }
                            parts = line.Split(new char[] { ',' }, StringSplitOptions.None);
                            columnCount = parts.Length;
                            for (int i = 0; i < columnCount; i++)
                            {
                                column = new DataColumn();
                                column.ColumnName = parts[i];
                                table.Columns.Add(column);
                            }
                            column = new DataColumn();
                            column.ColumnName = "hidden";
                            table.Columns.Add(column);
                        }
                        else
                        {
                            if (!Regex.IsMatch(line, linePattern))
                            {
                                throw new MockException("Data is invalid", "Common.Load");
                            }
                            parts = line.Split(new char[] { ',' }, StringSplitOptions.None);
                            int cellCount = parts.Length;
                            if (columnCount != cellCount)
                            {
                                throw new MockException("Data is invalid", "Common.Load");
                            }
                            row = table.NewRow();
                            for (int i = 0; i < cellCount; i++)
                            {
                                row[i] = parts[i];
                            }
                            row["hidden"] = Convert(row.ItemArray);
                            if (row["hidden"] == null)
                            {
                                throw new MockException("Hidden value is null", "Common.Load");
                            }
                            table.Rows.Add(row);
                        }
                        index++;
                    }
                }
                Tables.Add(table);
            }
            catch (IOException exception)
            {
                throw new MockException(exception.Message, "Common");
            }
            catch (OutOfMemoryException exception)
            {
                throw new MockException(exception.Message, "Common");
            }
            catch (ArgumentNullException exception)
            {
                throw new MockException(exception.Message, "Common");
            }
            catch (ArgumentException exception)
            {
                throw new MockException(exception.Message, "Common");
            }
            catch (DuplicateNameException exception)
            {
                throw new MockException(exception.Message, "Common");
            }
            catch (InvalidExpressionException exception)
            {
                throw new MockException(exception.Message, "Common");
            }
            catch (NoNullAllowedException exception)
            {
                throw new MockException(exception.Message, "Common");
            }
            catch (ConstraintException exception)
            {
                throw new MockException(exception.Message, "Common");
            }
            finally
            {
                if (reader != null) reader.Close();
            }
        }

        /// <summary>
        /// Converts the specified values.
        /// </summary>
        /// <param name="values">The values.</param>
        /// <returns></returns>
        private static string Convert(object[] values)
        {
            if (values != null)
            {
                try
                {
                    string convertedValue = "";
                    foreach (object value in values)
                    {
                        if (value is string)
                        {
                            convertedValue += String.Format(CultureInfo.CurrentCulture, "{0}\t", value);
                        }
                    }
                    return convertedValue;
                }
                catch (ArgumentNullException exception)
                {
                    throw new MockException(exception.Message, "Common");
                }
                catch (FormatException exception)
                {
                    throw new MockException(exception.Message, "Common");
                }
            }
            return null;
        }

        /// <summary>
        /// Converts the pattern.
        /// </summary>
        /// <param name="pattern">The pattern.</param>
        /// <returns></returns>
        private static string ConvertPattern(string pattern)
        {
            if (pattern != null)
            {
                try
                {
                    string convertedPattern = "";
                    int patternSize = pattern.Length;
                    for (int i = 0; i < patternSize; i++)
                    {
                        if (pattern[i] == ',')
                        {
                            convertedPattern += ".+";
                            if (i != (patternSize - 1))
                            {
                                convertedPattern += ",";
                            }
                        }
                    }
                    return convertedPattern;
                }
                catch (ArgumentNullException exception)
                {
                    throw new MockException(exception.Message, "Common");
                }
                catch (FormatException exception)
                {
                    throw new MockException(exception.Message, "Common");
                }
            }
            return null;
        }
    }
}